﻿//Dangerous River Source Code
//End game method
//Playing sounds when get good boost

namespace DangerousRiver
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using System.IO;
    using System.Media;

    struct Boat
    {
        public int x;
        public int y;
        public Boat(int xPoit, int yPoit)   // constructor of the boat.
        {
            this.x = xPoit;
            this.y = yPoit;
        }
    }
    struct Coast
    {
        public int x;
        public int y;
        public int size;
        public string shape;

        public Coast(int xPoint, int yPoint, int lineSize, string lineShape)
        {
            this.x = xPoint;
            this.y = yPoint;
            this.size = lineSize;
            this.shape = lineShape;
        }
    }
    struct Bonus
    {
        public int x;
        public int y;
        public char c;
        public ConsoleColor color;

        public Bonus(int xPoint, int yPoint, char symbol, ConsoleColor color)
        {
            this.x = xPoint;
            this.y = yPoint;
            this.c = symbol;
            this.color = color;
        }
    }
    class Game
    {
        public static int playGround = 40;
        public static int score = 0;
        public static Random randomGen = new Random();
        public static int windowHeight = 30;
        public static int windowWidth = 60;
        public static int leftSize = 10;          //Coast size
        public static int rightSize = 10;
        public static int indexOfCoastChange = 0; //Variables for coast algorithm
        public static int indexBreak = 9;
        public static double speed = 50.0;        //Speed of the boat     
        public static double acceleration = 0.5;
        public static int lives = 3;
        public static string[] directions = { "left", "right", "straight" };    //directions for coast algorithm
        public static string direction = "straight";                            //starting direction
        public static bool hitted = false;
        public static bool dead = false;
        private static List<Bonus> bonusList = new List<Bonus>(windowHeight);   //
        private static List<Coast> leftLineList = new List<Coast>(windowHeight);//Coast lines
        private static List<Coast> rightLineList = new List<Coast>(windowHeight);
        private static Boat boat = new Boat(playGround / 2, Console.WindowHeight - 2); // create boat
        private static int freeSpace;           //river width

        static void PrintOnPosition(int x, int y, char c, ConsoleColor color = ConsoleColor.Gray)
        {
            Console.SetCursorPosition(x, y);
            Console.ForegroundColor = color;
            Console.Write(c);
        }

        static void PrintStringOnPosition(int x, int y, string str, ConsoleColor color = ConsoleColor.Gray)
        {
            Console.SetCursorPosition(x, y);
            Console.ForegroundColor = color;
            Console.Write(str);
        }
        // TO DO: PRINT that the game is over and the score, if highscore add it to highscore list.
        //print a user choise options:  new game, main menu, exit game.

        static void ChangeCoastDirection()
        {
            if (direction == "left")
            {
                leftSize--;
                rightSize++;
            }
            else if (direction == "right")
            {
                leftSize++;
                rightSize--;
            }

            if (indexOfCoastChange == indexBreak)
            {
                direction = directions[randomGen.Next(0, 3)];
                if (direction == "left")
                {
                    if (leftSize < 2)
                    {
                        direction = "right";
                        indexBreak = randomGen.Next(1, 10);
                    }
                    indexBreak = randomGen.Next(1, leftSize);
                }
                else if (direction == "right")
                {
                    if (rightSize < 2)
                    {
                        direction = "left";
                        indexBreak = randomGen.Next(1, 10);
                    }
                    else
                    {
                        indexBreak = randomGen.Next(1, rightSize);
                    }

                }
                else if (direction == "straight")
                {
                    indexBreak = randomGen.Next(1, 10);
                }
                indexOfCoastChange = 0;
            }
        }

        static void MoveBoat(ConsoleKeyInfo CurrentPressedKey, ref Boat currentBoat)
        {
            if (CurrentPressedKey.Key == ConsoleKey.LeftArrow)
            {
                if (currentBoat.x - 1 >= 0)
                {
                    currentBoat.x--;
                }
            }
            else if (CurrentPressedKey.Key == ConsoleKey.RightArrow)
            {
                if (currentBoat.x + 1 < playGround)
                {
                    currentBoat.x++;
                }
            }
            else if (CurrentPressedKey.Key == ConsoleKey.UpArrow)
            {
                if (currentBoat.y - 1 > 0)
                {
                    currentBoat.y--;
                }
            }
            else if (CurrentPressedKey.Key == ConsoleKey.DownArrow)
            {
                if (currentBoat.y + 2 < windowHeight)
                {
                    currentBoat.y++;
                }
            }
        }

        static void MoveRiver(ref Boat currentBoat, List<Coast> leftLineList, List<Coast> rightLineList, ref List<Coast> currentLeftCoastLines, ref List<Coast> currentRightCoastLines)
        {
            for (int i = 0; i < leftLineList.Count; i++)
            {
                Coast oldLeftLine = leftLineList[i];
                Coast newLeftLine = new Coast
                    (oldLeftLine.x, oldLeftLine.y + 1, oldLeftLine.size, oldLeftLine.shape); //y+1

                Coast oldRightLine = rightLineList[i];
                Coast newRightLine = new Coast
                    (oldRightLine.x, oldRightLine.y + 1, oldRightLine.size, oldRightLine.shape); //y+1

                //TO DO: check if hit needs more attention when we add boosts
                if ((newLeftLine.y == currentBoat.y) && (newLeftLine.size > currentBoat.x) ||
                    (newRightLine.y == currentBoat.y) && (newRightLine.x <= currentBoat.x + 1))
                {
                    hitted = true;
                    bonusList.Clear();
                    //Playing the crashing sound
                    ThreadPool.QueueUserWorkItem(SoundEngine.PlayCrashSound);
                    //-------------------------
                    lives--;
                    if (lives == 0)
                    {

                        dead = true;
                    }
                    break;
                }

                if (newLeftLine.y < Console.WindowHeight)
                {
                    currentLeftCoastLines.Add(newLeftLine);
                    currentRightCoastLines.Add(newRightLine);
                }
            }
        }

        static void MoveBonus(ref Boat boat, ref List<Bonus> currentBonus)
        {

            for (int i = 0; i < bonusList.Count; i++)
            {
                Bonus oldBonus = bonusList[i];
                Bonus newBonus = new Bonus
                    (oldBonus.x, oldBonus.y + 1, oldBonus.c, oldBonus.color);

                // @: lower speed, $: bonus to score, +: add life, ^: speed up, *: mine, #: rock
                if (newBonus.c == '@' && (newBonus.x == boat.x + 1 || newBonus.x == boat.x)
                    && ((newBonus.y == boat.y) || newBonus.y == boat.y + 1))
                {
                    speed -= 15;
                    ThreadPool.QueueUserWorkItem(SoundEngine.PlayGoodBoost);
                    continue;
                }
                else if (newBonus.c == '$' && (newBonus.x == boat.x + 1 || newBonus.x == boat.x)
                    && ((newBonus.y == boat.y) || newBonus.y == boat.y + 1))
                {
                    score += 50;
                    ThreadPool.QueueUserWorkItem(SoundEngine.PlayGoodBoost);
                    continue;
                }
                else if (newBonus.c == '+' && (newBonus.x == boat.x + 1 || newBonus.x == boat.x)
                    && ((newBonus.y == boat.y) || newBonus.y == boat.y + 1))
                {
                    lives++;
                    ThreadPool.QueueUserWorkItem(SoundEngine.PlayGoodBoost);
                    continue;
                }
                else if (newBonus.c == '^' && (newBonus.x == boat.x + 1 || newBonus.x == boat.x)
                    && ((newBonus.y == boat.y) || newBonus.y == boat.y + 1))
                {
                    speed += 20;
                    ThreadPool.QueueUserWorkItem(SoundEngine.PlayBadBoost);
                    continue;
                }
                else if (newBonus.c == '*' && (newBonus.x == boat.x + 1 || newBonus.x == boat.x)
                         && ((newBonus.y == boat.y) || newBonus.y == boat.y + 1))
                {
                    lives--;
                    ThreadPool.QueueUserWorkItem(SoundEngine.PlayCrashSound);

                    if (lives == 0)
                    {
                        dead = true;
                        Thread.Sleep(800);
                        break;
                    }
                    hitted = true;
                    continue;
                }
                else if (newBonus.c == '#' && (newBonus.x == boat.x + 1 || newBonus.x == boat.x)
                        && ((newBonus.y == boat.y) || newBonus.y == boat.y + 1))
                {
                    lives--;
                    ThreadPool.QueueUserWorkItem(SoundEngine.PlayCrashSound);

                    if (lives == 0)
                    {
                        dead = true;
                        Thread.Sleep(800);
                        break;
                    }
                    hitted = true;
                    continue;
                }


                if (newBonus.y < Console.WindowHeight)
                {
                    currentBonus.Add(newBonus);
                }
            }
        }

        static void EndGame()
        {
            Console.Clear();

            //TO DO: Check if it is a highscore and save it !!!

            //Print Game Over and Score
            PrintStringOnPosition(windowWidth / 2 - 6, windowHeight / 2 - 6, "GAME OVER !!!", ConsoleColor.Red);
            string finalScore = "Score: " + score + " points.";
            PrintStringOnPosition(windowWidth / 2 - finalScore.Length / 2, windowHeight / 2 - 3, finalScore, ConsoleColor.White);
            Console.WriteLine();

            //Play endgame music
            ThreadPool.QueueUserWorkItem(SoundEngine.PlayEndGameMusicLoop);
            //DangerousRiver.SoundEngine.PlayEndGameMusicLoop();
            //Console.ReadKey(); //is used to pause         
            //------------------

            string playerName;

            //enter your initials for the high scores
            do
            {
                Console.Write("\n Enter your three initials for the high scores: ");
                playerName = Console.ReadLine();
            } 
            
            //prevent the index exception
            while (playerName.Length < 3);
            
            //cut the string down to three, if it's longer
            playerName = playerName.Substring(0, 3).ToUpper();

            ScoreEngine.SaveScore(playerName, score);
            Console.Clear();
            Menu.CallMenu();
        }

        static void CreateBonus()
        {
            int chance = randomGen.Next(0, 100);

            if (chance > 71 && chance < 75)
            {
                Bonus newBonus = new Bonus(randomGen.Next(leftSize, leftSize + freeSpace), -1, '@', ConsoleColor.Cyan);
                bonusList.Add(newBonus);
            }
            else if (chance > 88 && chance < 95)
            {
                Bonus newBonus = new Bonus(randomGen.Next(leftSize, leftSize + freeSpace), -1, '$', ConsoleColor.Yellow);
                bonusList.Add(newBonus);
            }
            else if (97 < chance && chance < 100)
            {
                Bonus newBonus = new Bonus(randomGen.Next(leftSize, leftSize + freeSpace), -1, '+', ConsoleColor.Green);
                bonusList.Add(newBonus);
            }
            else if (chance < 12 && chance > 8)
            {
                Bonus newBonus = new Bonus(randomGen.Next(leftSize, leftSize + freeSpace), -1, '*', ConsoleColor.Magenta);
                bonusList.Add(newBonus);
            }
            else if (chance > 5 && chance < 9)
            {
                Bonus newBonus = new Bonus(randomGen.Next(leftSize, leftSize + freeSpace), -1, '^', ConsoleColor.Red);
                bonusList.Add(newBonus);
            }
            else if (chance > 12 && chance < 21)
            {
                Bonus newBonus = new Bonus(randomGen.Next(leftSize, leftSize + freeSpace), -1, '#', ConsoleColor.White);
                bonusList.Add(newBonus);
            }
        }

        public static void Gameplay()
        {
            while (true)
            {
                //Used when player hitted or lives run out.
                hitted = false;
                dead = false;

                //Increase speed and score
                speed += acceleration;
                if (speed > 200)
                {
                    speed = 200;
                }
                score++;

                //Algorithm for better representing of the coast line
                ChangeCoastDirection();
                indexOfCoastChange++;

                freeSpace = playGround - leftSize - rightSize; //to see how wide is the river

                //Add left coast
                Coast leftCoast = new Coast(0, -1, leftSize, new string('#', leftSize));
                leftLineList.Add(leftCoast);

                //Add right coast
                Coast rightCoast = new Coast(playGround - rightSize, -1, rightSize, new string('#', rightSize));
                rightLineList.Add(rightCoast);
                
                //Move boat
                while (Console.KeyAvailable)   //change if to while makes boat move faster if you press and hond arrow keys
                {
                    ConsoleKeyInfo pressedKey = Console.ReadKey(true);

                    MoveBoat(pressedKey, ref boat);
                }

                //Move river
                List<Coast> leftCoastLines = new List<Coast>(windowHeight);
                List<Coast> rightCoastLines = new List<Coast>(windowHeight);

                MoveRiver(ref boat, leftLineList, rightLineList, ref leftCoastLines, ref rightCoastLines);

                leftLineList = leftCoastLines;
                rightLineList = rightCoastLines;

                //Create Bonus 

                CreateBonus();

                //Move Bonus
                List<Bonus> currentBonus = new List<Bonus>();
                MoveBonus(ref boat, ref currentBonus);
                bonusList = currentBonus;

                //When dead break.
                if (dead)
                {

                    DefaultValues();
                    //Needed because of a sound engine problem
                    //if deleted background music plays instead of end game music
                    EndGame();
                    break;
                }

                //Clear console
                Console.Clear();

                //If hitted display red X 
                if (hitted == true)
                {
                    PrintOnPosition(boat.x, boat.y, 'X', ConsoleColor.Red);
                }
                else
                {
                    //Redraw playfield:
                    //Print boat
                    PrintStringOnPosition(boat.x, boat.y, "/\\", ConsoleColor.DarkCyan);
                    PrintStringOnPosition(boat.x, boat.y + 1, "\\/", ConsoleColor.DarkCyan);
                    //Print coast
                    for (int i = 0; i < leftLineList.Count; i++)
                    {
                        PrintStringOnPosition(0, leftLineList[i].y, leftLineList[i].shape, ConsoleColor.DarkGreen);
                        PrintStringOnPosition(rightLineList[i].x, rightLineList[i].y, rightLineList[i].shape, ConsoleColor.DarkGreen);
                    }
                    //print bonuses
                    for (int i = 0; i < bonusList.Count; i++)
                    {
                        PrintOnPosition(bonusList[i].x, bonusList[i].y, bonusList[i].c, bonusList[i].color);
                    }
                }
                //Print player stats
                PrintStringOnPosition(playGround + 3, 2, "Lives: " + lives, ConsoleColor.White);
                PrintStringOnPosition(playGround + 3, 3, "Your Score: " + score.ToString(), ConsoleColor.White);
                PrintStringOnPosition(playGround + 3, 4, "Speed: " + speed, ConsoleColor.White);
                //**************

                //Print description of all bonuses
                PrintStringOnPosition(playGround + 3, 10, "Bonuses legend", ConsoleColor.White);
                PrintStringOnPosition(playGround + 3, 12, "@ - Speed reduce", ConsoleColor.Cyan);
                PrintStringOnPosition(playGround + 3, 13, "$ - Score bonus", ConsoleColor.Yellow);
                PrintStringOnPosition(playGround + 3, 14, "+ - Add live", ConsoleColor.Green);
                PrintStringOnPosition(playGround + 3, 15, "^ - Speed rise", ConsoleColor.Red);
                PrintStringOnPosition(playGround + 3, 16, "* - Mine", ConsoleColor.Magenta);
                PrintStringOnPosition(playGround + 3, 17, "# - Rock", ConsoleColor.White);
                //***************

                //Slow program
                Thread.Sleep((int)(250 - speed));
            }
        }

        static void DefaultValues()
        {
            leftSize = 10;
            rightSize = 10;
            lives = 3;
            bonusList.Clear();
            leftLineList.Clear();
            rightLineList.Clear();
            speed = 50.0;
            boat.x = playGround / 2;
            boat.y = Console.WindowHeight - 2;
        }
        static void Main()
        {
            // Hide the cursor in specific situations
            Console.CursorVisible = false;

            //Size of screen
            Console.WindowHeight = windowHeight;
            Console.WindowWidth = windowWidth;

            //Loop the background music
            SoundEngine.PlayBackGroundMusicLoop();
            //------------------------

            Console.BufferHeight = Console.WindowHeight;
            Console.BufferWidth = Console.WindowWidth;

            Menu.CallMenu();    //<-- If active, comment Gameplay();
            // Gameplay();
        }
    }
}